#pragma once

#include <future>
#include <functional>
#include <unordered_map>
#include <mutex>
#include <memory>

#include "../common/net.hpp"
#include "../common/message.hpp"
#include "../common/fields.hpp"

namespace JsonMuduoRPC
{
    namespace client
    {
        class Requestor
        {
        public:
            using ptr = std::shared_ptr<Requestor>;
            using RequestCallBack = std::function<void(const BaseMessage::ptr &)>;
            using AsyncResponse = std::future<BaseMessage::ptr>;

            struct RequestDescribe
            {
                using ptr = std::shared_ptr<RequestDescribe>;

                BaseMessage::ptr request;
                RType rtype;
                std::promise<BaseMessage::ptr> response;
                RequestCallBack cb_request;
            };

            // 发送请求，以异步的方式获得响应
            bool send(const BaseConnection::ptr &con, const BaseMessage::ptr &req, AsyncResponse &async_rsp)
            {
                RequestDescribe::ptr req_desc = newRequestDescribe(req, RType::REQ_ASYNC);
                if (req_desc.get() == nullptr)
                {
                    LOG(ERROR, "构造请求描述对象失败！");
                    return false;
                }

                con->send(req);
                async_rsp = req_desc->response.get_future();

                return true;
            }

            //发送请求，以同步的方式获得响应
            bool send(const BaseConnection::ptr &con, const BaseMessage::ptr &req, BaseMessage::ptr& rsp)
            {
                AsyncResponse async_rsp;
                bool ret = send(con, req, async_rsp);
                if (ret == false)
                    return false;
                
                rsp = async_rsp.get();
                return true;
            }


            // 发送请求，以回调的方式获得响应
            bool send(const BaseConnection::ptr &con, const BaseMessage::ptr &req, RequestCallBack &cb)
            {
                RequestDescribe::ptr req_desc = newRequestDescribe(req, RType::REQ_CALLBACK, cb);
                if (req_desc.get() == nullptr)
                {
                    LOG(ERROR, "构造请求描述对象失败！");
                    return false;
                }

                con->send(req);

                return true;
            }

            // 处理响应
            void onResponse(const BaseConnection::ptr &con, const BaseMessage::ptr &rsp)
            {
                const std::string id = rsp->getId();
                RequestDescribe::ptr req_desc = getRequestDescribe(id);
                if (req_desc.get() == nullptr)
                {
                    LOG(ERROR, "收到响应 - %s, 但是未找到对应的请求描述!", id.c_str());
                    return;
                }

                if (req_desc->rtype == RType::REQ_ASYNC)
                    req_desc->response.set_value(rsp);
                else if (req_desc->rtype == RType::REQ_CALLBACK && req_desc->cb_request)
                    req_desc->cb_request(rsp);
                else
                    LOG(ERROR, "未知请求类型: %d", req_desc->rtype);

                deleteRequestDescribe(id);  //处理完一条请求就要删除，防止内存泄露
            }

        private:
            RequestDescribe::ptr newRequestDescribe(const BaseMessage::ptr &req, const RType &rtype,
                                                    const RequestCallBack &cb = RequestCallBack())
            {
                std::unique_lock<std::mutex> lock(_mutex);

                RequestDescribe::ptr req_desc = std::make_shared<RequestDescribe>();
                req_desc->request = req;
                req_desc->rtype = rtype;
                if (rtype == RType::REQ_CALLBACK && cb)
                    req_desc->cb_request = cb;

                _requests_map.insert(std::make_pair(req->getId(), req_desc));

                return req_desc;
            }

            RequestDescribe::ptr getRequestDescribe(const std::string &id)
            {
                std::unique_lock<std::mutex> lock(_mutex);

                auto it = _requests_map.find(id);
                if (it == _requests_map.end())
                    return RequestDescribe::ptr();

                return it->second;
            }

            void deleteRequestDescribe(const std::string &id)
            {
                std::unique_lock<std::mutex> lock(_mutex);
                _requests_map.erase(id);
            }

        private:
            std::mutex _mutex;
            std::unordered_map<std::string, RequestDescribe::ptr> _requests_map;
        };
    } // namespace client

}
